/******************************************************************************* * Copyright (c) 2000, 2015 IBM Corporation and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * IBM Corporation - initial API and implementation * James Blackburn - Bug 256316 getImageDescriptor() is not thread safe *******************************************************************************/ package org.eclipse.ui.internal.registry; import java.io.File; import java.io.Serializable; import org.eclipse.core.runtime.Assert; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IConfigurationElement; import org.eclipse.jface.resource.ImageDescriptor; import org.eclipse.swt.graphics.Image; import org.eclipse.swt.program.Program; import org.eclipse.ui.IEditorActionBarContributor; import org.eclipse.ui.IEditorDescriptor; import org.eclipse.ui.IEditorMatchingStrategy; import org.eclipse.ui.IEditorPart; import org.eclipse.ui.IMemento; import org.eclipse.ui.IPluginContribution; import org.eclipse.ui.ISharedImages; import org.eclipse.ui.internal.IWorkbenchConstants; import org.eclipse.ui.internal.WorkbenchImages; import org.eclipse.ui.internal.WorkbenchPlugin; import org.eclipse.ui.internal.misc.ProgramImageDescriptor; import org.eclipse.ui.internal.tweaklets.InterceptContributions; import org.eclipse.ui.internal.tweaklets.Tweaklets; import org.eclipse.ui.internal.util.Util; import org.eclipse.ui.plugin.AbstractUIPlugin; /** * @see IEditorDescriptor */ public final class EditorDescriptor implements IEditorDescriptor, Serializable, IPluginContribution { /** * Generated serial version UID for this class. * @since 3.1 */ private static final long serialVersionUID = 3905241225668998961L; // @issue the following constants need not be public; see bug 47600 /** * Open internal constant. Value <code>0x01</code>. */ public static final int OPEN_INTERNAL = 0x01; /** * Open in place constant. Value <code>0x02</code>. */ public static final int OPEN_INPLACE = 0x02; /** * Open external constant. Value <code>0x04</code>. */ public static final int OPEN_EXTERNAL = 0x04; private String editorName; private String imageFilename; private transient ImageDescriptor imageDesc; private transient Object imageDescLock = new Object(); private boolean testImage = true; private String className; private String launcherName; private String fileName; private String id = Util.ZERO_LENGTH_STRING; private boolean matchingStrategyChecked = false; private IEditorMatchingStrategy matchingStrategy; private Program program; //The id of the plugin which contributed this editor, null for external editors private String pluginIdentifier; private int openMode = 0; private transient IConfigurationElement configurationElement; /** * Create a new instance of an editor descriptor. Limited * to internal framework calls. * @param element * @param id2 */ /* package */EditorDescriptor(String id2, IConfigurationElement element) { setID(id2); setConfigurationElement(element); } /** * Create a new instance of an editor descriptor. Limited * to internal framework calls. */ /* package */ EditorDescriptor() { super(); } /** * Creates a descriptor for an external program. * * @param filename the external editor full path and filename * @return the editor descriptor */ public static EditorDescriptor createForProgram(String filename) { if (filename == null) { throw new IllegalArgumentException(); } EditorDescriptor editor = new EditorDescriptor(); editor.setFileName(filename); editor.setID(filename); editor.setOpenMode(OPEN_EXTERNAL); //Isolate the program name (no directory or extension) int start = filename.lastIndexOf(File.separator); String name; if (start != -1) { name = filename.substring(start + 1); } else { name = filename; } int end = name.lastIndexOf('.'); if (end != -1) { name = name.substring(0, end); } editor.setName(name); // get the program icon without storing it in the registry ImageDescriptor imageDescriptor = new ProgramImageDescriptor(filename, 0); editor.setImageDescriptor(imageDescriptor); return editor; } /** * Return the program called programName. Return null if it is not found. * @return org.eclipse.swt.program.Program */ private static Program findProgram(String programName) { for (Program program : Program.getPrograms()) { if (program.getName().equals(programName)) { return program; } } return null; } /** * Create the editor action bar contributor for editors of this type. * * @return the action bar contributor, or <code>null</code> */ public IEditorActionBarContributor createActionBarContributor() { // Handle case for predefined editor descriptors, like the // one for IEditorRegistry.SYSTEM_INPLACE_EDITOR_ID, which // don't have a configuration element. if (configurationElement == null) { return null; } // Get the contributor class name. String className = configurationElement .getAttribute(IWorkbenchRegistryConstants.ATT_CONTRIBUTOR_CLASS); if (className == null) { return null; } // Create the contributor object. IEditorActionBarContributor contributor = null; try { contributor = (IEditorActionBarContributor) WorkbenchPlugin .createExtension(configurationElement, IWorkbenchRegistryConstants.ATT_CONTRIBUTOR_CLASS); } catch (CoreException e) { WorkbenchPlugin.log("Unable to create editor contributor: " + //$NON-NLS-1$ id, e.getStatus()); } return contributor; } /** * Return the editor class name. * * @return the class name */ public String getClassName() { if (configurationElement == null) { return className; } return RegistryReader.getClassValue(configurationElement, IWorkbenchRegistryConstants.ATT_CLASS); } /** * Return the configuration element used to define this editor, or <code>null</code>. * * @return the element or null */ public IConfigurationElement getConfigurationElement() { return configurationElement; } /** * Create an editor part based on this descriptor. * * @return the editor part * @throws CoreException thrown if there is an issue creating the editor */ public IEditorPart createEditor() throws CoreException { Object extension = WorkbenchPlugin.createExtension(getConfigurationElement(), IWorkbenchRegistryConstants.ATT_CLASS); return ((InterceptContributions)Tweaklets.get(InterceptContributions.KEY)).tweakEditor(extension); } /** * Return the file name of the command to execute for this editor. * * @return the file name to execute */ public String getFileName() { if (program == null) { if (configurationElement == null) { return fileName; } return configurationElement.getAttribute(IWorkbenchRegistryConstants.ATT_COMMAND); } return program.getName(); } /** * Return the id for this editor. * * @return the id */ @Override public String getId() { if (program == null) { if (configurationElement == null) { return Util.safeString(id); } return Util.safeString(configurationElement.getAttribute(IWorkbenchRegistryConstants.ATT_ID)); } return Util.safeString(program.getName()); } /** * Return the image descriptor describing this editor. * * @return the image descriptor */ @Override public ImageDescriptor getImageDescriptor() { ImageDescriptor tempDescriptor = null; synchronized (imageDescLock) { if (!testImage) return imageDesc; if (imageDesc == null) { String imageFileName = getImageFilename(); String command = getFileName(); if (imageFileName != null && configurationElement != null) tempDescriptor = AbstractUIPlugin.imageDescriptorFromPlugin(configurationElement.getNamespaceIdentifier(), imageFileName); else if (command != null) tempDescriptor = WorkbenchImages.getImageDescriptorFromProgram(command, 0); } else tempDescriptor = imageDesc; if (tempDescriptor == null) { // still null? return default image imageDesc = WorkbenchImages.getImageDescriptor(ISharedImages.IMG_OBJ_FILE); testImage = false; return imageDesc; } } // Verifies that the image descriptor generates an image. If not, the descriptor is // replaced with the default image. // We must create the image without holding any locks, since there is a potential for deadlock // on Linux due to SWT's implementation of Image. See bugs 265028 and 256316 for details. Image img = tempDescriptor.createImage(false); if (img == null) // @issue what should be the default image? tempDescriptor = WorkbenchImages.getImageDescriptor(ISharedImages.IMG_OBJ_FILE); else img.dispose(); // <----- End of must-not-lock part // reenter synchronized block synchronized (imageDescLock) { // if another thread has set the image description, use it if (!testImage) return imageDesc; // otherwise set the image description we calculated above imageDesc = tempDescriptor; testImage = false; return imageDesc; } } /** * The Image to use to repesent this editor */ /* package */void setImageDescriptor(ImageDescriptor desc) { synchronized (imageDescLock) { imageDesc = desc; testImage = true; } } /** * The name of the image describing this editor. * * @return the image file name */ public String getImageFilename() { if (configurationElement == null) { return imageFilename; } return configurationElement.getAttribute(IWorkbenchRegistryConstants.ATT_ICON); } /** * Return the user printable label for this editor. * * @return the label */ @Override public String getLabel() { if (program == null) { if (configurationElement == null) { return editorName; } return configurationElement.getAttribute(IWorkbenchRegistryConstants.ATT_NAME); } return program.getName(); } /** * Returns the class name of the launcher. * * @return the launcher class name */ public String getLauncher() { if (configurationElement == null) { return launcherName; } return configurationElement.getAttribute(IWorkbenchRegistryConstants.ATT_LAUNCHER); } /** * Return the contributing plugin id. * * @return the contributing plugin id */ public String getPluginID() { if (configurationElement != null) { return configurationElement.getNamespace(); } return pluginIdentifier; } /** * Get the program for the receiver if there is one. * @return Program */ public Program getProgram() { return this.program; } @Override public boolean isInternal() { return getOpenMode() == OPEN_INTERNAL; } @Override public boolean isOpenInPlace() { return getOpenMode() == OPEN_INPLACE; } @Override public boolean isOpenExternal() { return getOpenMode() == OPEN_EXTERNAL; } /** * Load the object properties from a memento. * * @return <code>true</code> if the values are valid, <code>false</code> otherwise */ protected boolean loadValues(IMemento memento) { editorName = memento.getString(IWorkbenchConstants.TAG_LABEL); imageFilename = memento.getString(IWorkbenchConstants.TAG_IMAGE); className = memento.getString(IWorkbenchConstants.TAG_CLASS); launcherName = memento.getString(IWorkbenchConstants.TAG_LAUNCHER); fileName = memento.getString(IWorkbenchConstants.TAG_FILE); id = Util.safeString(memento.getString(IWorkbenchConstants.TAG_ID)); pluginIdentifier = memento.getString(IWorkbenchConstants.TAG_PLUGIN); Integer openModeInt = memento .getInteger(IWorkbenchConstants.TAG_OPEN_MODE); if (openModeInt != null) { openMode = openModeInt.intValue(); } else { // legacy: handle the older attribute names, needed to allow reading of pre-3.0-RCP workspaces boolean internal = Boolean.parseBoolean(memento .getString(IWorkbenchConstants.TAG_INTERNAL)); boolean openInPlace = Boolean.parseBoolean(memento .getString(IWorkbenchConstants.TAG_OPEN_IN_PLACE)); if (internal) { openMode = OPEN_INTERNAL; } else { if (openInPlace) { openMode = OPEN_INPLACE; } else { openMode = OPEN_EXTERNAL; } } } if (openMode != OPEN_EXTERNAL && openMode != OPEN_INTERNAL && openMode != OPEN_INPLACE) { WorkbenchPlugin .log("Ignoring editor descriptor with invalid openMode: " + this); //$NON-NLS-1$ return false; } String programName = memento .getString(IWorkbenchConstants.TAG_PROGRAM_NAME); if (programName != null) { this.program = findProgram(programName); } return true; } /** * Save the object values in a IMemento */ protected void saveValues(IMemento memento) { memento.putString(IWorkbenchConstants.TAG_LABEL, getLabel()); memento.putString(IWorkbenchConstants.TAG_IMAGE, getImageFilename()); memento.putString(IWorkbenchConstants.TAG_CLASS, getClassName()); memento.putString(IWorkbenchConstants.TAG_LAUNCHER, getLauncher()); memento.putString(IWorkbenchConstants.TAG_FILE, getFileName()); memento.putString(IWorkbenchConstants.TAG_ID, getId()); memento.putString(IWorkbenchConstants.TAG_PLUGIN, getPluginId()); memento.putInteger(IWorkbenchConstants.TAG_OPEN_MODE, getOpenMode()); // legacy: handle the older attribute names, needed to allow reading of workspace by pre-3.0-RCP eclipses memento.putString(IWorkbenchConstants.TAG_INTERNAL, String .valueOf(isInternal())); memento.putString(IWorkbenchConstants.TAG_OPEN_IN_PLACE, String .valueOf(isOpenInPlace())); if (this.program != null) { memento.putString(IWorkbenchConstants.TAG_PROGRAM_NAME, this.program.getName()); } } /** * Return the open mode of this editor. * * @return the open mode of this editor * @since 3.1 */ private int getOpenMode() { if (configurationElement == null) { // if we've been serialized, return our serialized value return openMode; } else if (getLauncher() != null) { // open using a launcer return EditorDescriptor.OPEN_EXTERNAL; } else if (getFileName() != null) { // open using an external editor return EditorDescriptor.OPEN_EXTERNAL; } else if (getPluginId() != null) { // open using an internal editor return EditorDescriptor.OPEN_INTERNAL; } else { return 0; // default for system editor } } /** * Set the class name of an internal editor. */ /* package */void setClassName(String newClassName) { className = newClassName; } /** * Set the configuration element which contributed this editor. */ /* package */void setConfigurationElement( IConfigurationElement newConfigurationElement) { configurationElement = newConfigurationElement; } /** * Set the filename of an external editor. */ /* package */void setFileName(String aFileName) { fileName = aFileName; } /** * Set the id of the editor. * For internal editors this is the id as provided in the extension point * For external editors it is path and filename of the editor */ /* package */void setID(String anID) { Assert.isNotNull(anID); id = anID; } /** * The name of the image to use for this editor. */ /* package */void setImageFilename(String aFileName) { imageFilename = aFileName; } /** * Sets the new launcher class name * * @param newLauncher the new launcher */ /* package */void setLauncher(String newLauncher) { launcherName = newLauncher; } /** * The label to show for this editor. */ /* package */void setName(String newName) { editorName = newName; } /** * Sets the open mode of this editor descriptor. * * @param mode the open mode * * @issue this method is public as a temporary fix for bug 47600 */ public void setOpenMode(int mode) { openMode = mode; } /** * The id of the plugin which contributed this editor, null for external editors. */ /* package */void setPluginIdentifier(String anID) { pluginIdentifier = anID; } /** * Set the receivers program. * @param newProgram */ /* package */void setProgram(Program newProgram) { this.program = newProgram; if (editorName == null) { setName(newProgram.getName()); } } /** * For debugging purposes only. */ @Override public String toString() { return "EditorDescriptor(id=" + getId() + ", label=" + getLabel() + ")"; //$NON-NLS-2$ //$NON-NLS-3$//$NON-NLS-1$ } @Override public String getLocalId() { return getId(); } @Override public String getPluginId() { return getPluginID(); } @Override public IEditorMatchingStrategy getEditorMatchingStrategy() { if (matchingStrategy == null && !matchingStrategyChecked) { matchingStrategyChecked = true; if (program == null && configurationElement != null) { if (configurationElement.getAttribute(IWorkbenchRegistryConstants.ATT_MATCHING_STRATEGY) != null) { try { matchingStrategy = (IEditorMatchingStrategy) WorkbenchPlugin.createExtension(configurationElement, IWorkbenchRegistryConstants.ATT_MATCHING_STRATEGY); } catch (CoreException e) { WorkbenchPlugin.log("Error creating editor management policy for editor id " + getId(), e); //$NON-NLS-1$ } } } } return matchingStrategy; } }